Skip to content

Modals

Just about every modern web application needs a good <Modal> component.

A modal is an in-window popup that displays a message. Sometimes, it has buttons to confirm an action, in which case it's often called a “dialog box”.

Click the button to open an example modal:

If you're reasonably comfortable with React and CSS, this sort of UI component might not seem too difficult. On the React side, we'll need an isOpen state variable and some conditional rendering. On the CSS side, a fixed-position parent with a centered child.

Modals are deceptively tricky, though. They have complex usability expectations and accessibility requirements.

Building a modal

In the sandbox below, you've been given an incomplete Modal component. Your job is to see how many issues you can find + fix.

This is an open-ended exercise. There's no acceptance criteria, because part of the challenge is seeing if you can spot the problems.

Some hints + tips:

  • Feel free to change any of the code, including the CSS — nothing is set in stone!
  • You're allowed to install + use NPM dependencies if you want. Click the “” icon to open this playground in CodeSandbox, where you'll be able to install whichever NPM packages you wish.
  • Not sure where to start? Try to use the modal without touching your mouse/trackpad, or check out the WAI-ARIA guidelines for modals/dialogs.
  • If you'd prefer, you can skip straight to the video below, where we'll walk through the various issues and how to fix them.

Code Playground

import React from 'react';
import { X as Close } from 'react-feather';

import styles from './Modal.module.css';

function Modal({ handleDismiss, children }) {
return (
<div className={styles.wrapper}>
<div className={styles.backdrop} />
<div className={styles.dialog}>
<div
className={styles.closeBtn}
onClick={handleDismiss}
>
<Close />
</div>
{children}
</div>
</div>
);
}

export default Modal;

Alright, let's dig into this!

Video Summary

A few years ago, Ryan Florence (co-creator of React Router, Remix, Reach UI) gave a conference talk called The Curse of React.

The core idea in his talk is that once you get comfortable with React, it starts to feel a bit like a super power. Building components like modals feels a lot easier than it did, back in the jQuery days. Instead of reaching for a library, many developers choose to implement these things ourselves, from scratch.

The problem is that the libraries we used to depend on did a whole bunch of stuff for us that we weren't even aware of, critical usability and accessibility things.

The modal we see above works pretty well for mouse users, but it fails catastrophically for folks who use a keyboard or screen reader. We have a ton of work to do to build something that could be considered production-ready.

Here's a list of the problems we address in the video:

  • Being able to focus the modal's close button with the keyboard, by changing the <div> to <button> (and removing some default button styles).
  • Automatically focusing the close button when the modal is opened
  • Restoring focus to the last-focused element when the modal is closed
  • Locking focus to within the modal, while the modal is open
  • Disabling the scrollbar while the modal is open
  • Clicking the overlay with the mouse should close the modal
  • Hitting the "Escape" key should close the modal
  • Folks using screen readers should be aware that they're within a dialog box, with a title.
  • Folks using screen readers should be aware that the close button will dismiss the modal, with some visually-hidden text.

(You can see the final code below, or watch the video to see how I tackle each problem individually.)

This is a lot of stuff, and honestly I might've even missed some things. I'd say I have intermediate-level accessibility skills/knowledge. Getting this right is super hard. In the lessons that follow, we're going to see how accessibility-focused libraries can help us out here.

That said, I do think it's worth having the skills to solve these sorts of problems from scratch. Not all problems can be solved with a library, and it really helps to be able to know how to build stuff like this from scratch.

In the video above, I reference a talk by Ryan Florence: “The Curse of React”.

Here's our final implementation from the video:

Code Playground

import React from 'react';

import useToggle from './use-toggle';
import Modal from './Modal';

function App() {
const [isModalOpen, toggleIsModalOpen] = useToggle(false);
return (
<>
{isModalOpen && (
<Modal
title="Hello world"
handleDismiss={toggleIsModalOpen}
>
This is an example modal! It includes <a href="">several</a> <a href="">different</a> <a href="">links</a>.
</Modal>
)}
<button onClick={toggleIsModalOpen}>
Toggle modal
</button>
<p>
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
</p>
<p>
It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<p>
<a href="https://www.lipsum.com/">Read more on lipsum.com</a>.
</p>
</>
)
}

export default App;